// ----------------
// Project:
// ESA Elevator
// ----------------
//
// Description:
// ----------------
// sensor_if.v testbench
//
// Version History:
// ----------------
// 140116: changed unit in comment of VELOCITY_THRESHOLD

`timescale 1ns / 1ps

module sensor_if_tb_public;

  //****************** SIMULATION PARAMETERS *******************//  
  localparam CLK_PERIOD            =  50; // [ns] -> 20 MHz
  localparam VELOCITY_THRESHOLD    = 2000; // velocity to apply the emergency break [cm/s]
  localparam MAX_WEIGHT            =  500; // maximum cabin load [kg]
  //***********************************************************//
  

  //*************** DERIVED SIMULATION PARAMETERS *************// 
  localparam VELOCITY_SENSOR_BITS  = min_1(ceil_log2(VELOCITY_THRESHOLD));
  localparam WEIGHT_SENSOR_BITS    = min_1(ceil_log2(MAX_WEIGHT));
  //***********************************************************//
  

  //********************* MODULE INPUTS ***********************// 
  reg CLK;
  reg RESET;
  reg                               DOOR_LIGHT_CURTAIN_IN;         // 1 if object in the way
  reg  [(VELOCITY_SENSOR_BITS-1):0] VELOCITY_SENSOR_IN;            // speed [cm/s]
  reg  [(WEIGHT_SENSOR_BITS-1)  :0] WEIGHT_SENSOR_IN;              // weight [kg]
  reg                               SMOKE_PARTICLE_SENSOR_IN;      // 1 if smoke detected
  //***********************************************************//  


  //********************* MODULE OUTPUTS **********************//
  wire  OBJECT_DETECTED;   // 0 if door is free
  wire  SPEED_OK;          // 1 if speed is ok
  wire  WEIGHT_OK;         // 1 if weight is ok
  wire  SMOKE_DETECTED;    // 0 if smoke is ok
  //***********************************************************// 


  //******************* UUT INSTANTIATION *********************// 
  sensor_if #(.WEIGHT_BITS        (WEIGHT_SENSOR_BITS),
              .MAX_WEIGHT         (MAX_WEIGHT), 
              .VELOCITY_BITS      (VELOCITY_SENSOR_BITS), 
              .VELOCITY_THRESHOLD (VELOCITY_THRESHOLD)) 
  
             uut (
                  .CLK(CLK),
                  .RESET(RESET),
                  .DOOR_LIGHT_CURTAIN_IN(DOOR_LIGHT_CURTAIN_IN),
                  .VELOCITY_SENSOR_IN(VELOCITY_SENSOR_IN),
                  .WEIGHT_SENSOR_IN(WEIGHT_SENSOR_IN),
                  .SMOKE_PARTICLE_SENSOR_IN(SMOKE_PARTICLE_SENSOR_IN),
                  
                  .DOOR_LIGHT_CURTAIN_OUT(OBJECT_DETECTED),
                  .VELOCITY_SENSOR_OUT(SPEED_OK),
                  .WEIGHT_SENSOR_OUT(WEIGHT_OK),
                  .SMOKE_PARTICLE_SENSOR_OUT(SMOKE_DETECTED)); 
  //***********************************************************//
  
  
  integer COUNT;                                 

  //******************* 20 MHz CLOCK SIGNAL *******************//   
  always begin 
    #25 CLK = ~CLK;
  end
  //***********************************************************// 


  //********************* TEST INITIATION *********************//  
  initial begin
    COUNT = 0;
    // Initialize Inputs
    CLK                = 0;
    RESET              = 1;
    DOOR_LIGHT_CURTAIN_IN    = 0;  // 1 if object in the way
    VELOCITY_SENSOR_IN       = 0;  // speed [cm/s]
    WEIGHT_SENSOR_IN         = 0;  // weight [kg]
    SMOKE_PARTICLE_SENSOR_IN = 0;  // 1 if smoke detected
    // Wait 75 ns for global reset to finish
    #(3*CLK_PERIOD/2);
    RESET = 0;
    $display("************ STARTING SIMULATION ************");  
    #(CLK_PERIOD);
    //--------------------------
    // DOOR LIGHT CURTAIN
    //--------------------------
    DOOR_LIGHT_CURTAIN_IN    = 1; 
    VELOCITY_SENSOR_IN       = 0; 
    WEIGHT_SENSOR_IN         = 0; 
    SMOKE_PARTICLE_SENSOR_IN = 0;   
    while(~OBJECT_DETECTED && (COUNT < 256)) begin
     #(CLK_PERIOD);
     COUNT = COUNT + 1;
    end
    if (OBJECT_DETECTED) begin
      $display("light curtain activated after %d clock cycles", COUNT);
    end
    else begin
      $display("error: light curtain detection");
    end
    #(10*CLK_PERIOD);
    //--------------------------
    // VELOCITY_SENSOR
    //--------------------------
    COUNT                    =    0;
    DOOR_LIGHT_CURTAIN_IN    =    0; 
    VELOCITY_SENSOR_IN       = VELOCITY_THRESHOLD - 1; 
    WEIGHT_SENSOR_IN         =    0; 
    SMOKE_PARTICLE_SENSOR_IN =    0;
    while(SPEED_OK && (COUNT < 256)) begin
     #(CLK_PERIOD);
     COUNT = COUNT + 1;
    end
    if (~SPEED_OK) begin
      $display("error: velocity should be in range");
    end
    else begin
      $display("velocity sensor still ok after %d cycles", COUNT);
    end
    #(10*CLK_PERIOD);
    COUNT                   =    0; 
    VELOCITY_SENSOR_IN      = VELOCITY_THRESHOLD + 1;
    while(SPEED_OK && (COUNT <= 256)) begin
     #(CLK_PERIOD);
     COUNT = COUNT + 1;
    end
    if (~SPEED_OK) begin
      $display("emergency brake activated, after %d clock cycles", COUNT);
    end
    else begin
      $display("error: velocity sensor detection error");
    end
    #(10*CLK_PERIOD);
    //--------------------------
    // WEIGHT_SENSOR
    //--------------------------
    COUNT                   =    0; 
    DOOR_LIGHT_CURTAIN_IN    =   0; 
    VELOCITY_SENSOR_IN       =   0; 
    WEIGHT_SENSOR_IN         = MAX_WEIGHT - 1; 
    SMOKE_PARTICLE_SENSOR_IN =   0; 
    while(WEIGHT_OK && (COUNT < 256)) begin
     #(CLK_PERIOD);
     COUNT = COUNT + 1;
    end
    if (~WEIGHT_OK) begin
      $display("error: weight should be in range");
    end
    else begin
      $display("weight sensor still ok after %d cycles", COUNT);
    end
    #(10*CLK_PERIOD);
    COUNT                   =    0; 
    WEIGHT_SENSOR_IN        = MAX_WEIGHT + 1; 
    while(WEIGHT_OK && (COUNT < 256)) begin
     #(CLK_PERIOD);
     COUNT = COUNT + 1;
    end
    if (~WEIGHT_OK) begin
      $display("too much weight detected, after %d clock cycles", COUNT);
    end
    else begin
      $display("error: weight sensor detection error");
    end
    #(10*CLK_PERIOD);
    //--------------------------
    // SMOKE_PARTICLE_SENSOR
    //--------------------------
    COUNT                    = 0; 
    DOOR_LIGHT_CURTAIN_IN    = 0; 
    VELOCITY_SENSOR_IN       = 0; 
    WEIGHT_SENSOR_IN         = 0; 
    SMOKE_PARTICLE_SENSOR_IN = 1; 
    while(~SMOKE_DETECTED && (COUNT < 256)) begin
     #(CLK_PERIOD);
     COUNT = COUNT + 1;
    end
    if (SMOKE_DETECTED) begin
      $display("smoke detected after %d clock cycles", COUNT);
    end
    else begin
      $display("error: smoke not detected");
    end
    $display("************ SIMULATION COMPLETE ************");
    $finish;
  end
  //***********************************************************// 
initial begin
  #(100000*CLK_PERIOD)
  $display("************ SIMULATION KILLED BECAUSE OF ERROR ************");
  $finish;
end
 

  //******************* PARAMETER FUNCTIONS *******************//
  //ceil of the log base 2
  function integer ceil_log2;
    input [31:0] value;
    for (ceil_log2=0; value>0; ceil_log2=ceil_log2+1)
      value = value>>1;
  endfunction
  
  // value cannot be less than 1
  function integer min_1;
    input [31:0] value;
    if (value == 0)
      min_1 = 1;
    else
      min_1 = value;
  endfunction 
  //***********************************************************//  
       
endmodule

